package listeners

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/tjfoc/gmsm/sm2"
	"github.com/tjfoc/gmsm/sm3"
	"github.com/tjfoc/gmsm/sm4"
	"net/http"
	"strings"
)

func RegisterSM234(router *gin.Engine) {
	rootGroup := getRootGroup(router)
	cci := rootGroup.Group("/sm")

	cci.POST("/2/encryption", doSm2Encryption)
	cci.POST("/2/decryption", doSm2Decryption)

	cci.POST("/3/sum", doSm3Sum)

	cci.POST("/4/encryption", doSm4Encryption)
	cci.POST("/4/decryption", doSm4Decryption)
}

type EncryptionAndDecryption struct {
	Key        string `json:"key"` // more than 40 character
	Text       string `json:"text"`
	CipherText string `json:"clipherText"`
}

func doSm4Encryption(ctx *gin.Context) {
	ead := new(EncryptionAndDecryption)
	if err := ctx.BindJSON(&ead); err != nil {
		ctx.JSON(http.StatusBadRequest, fmt.Sprintf("could not find matching EncryptionAndDecryption form: %v", *ead))
		return
	}
	//iv := []byte("0000000000000000")
	//err = SetIV(iv)//设置SM4算法实现的IV值,不设置则使用默认值
	cipherBytes, err := sm4.Sm4Ecb([]byte(ead.Key), []byte(ead.Text), true) //sm4Ecb模式pksc7填充加密
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, fmt.Sprintf("Sm4Ecb error: %v", err))
		return
	}

	ead.CipherText = hex.EncodeToString(cipherBytes)
	eadBytes, err := json.Marshal(*ead)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, fmt.Sprintf("ead marshal error: %v", err))
		return
	}

	ctx.JSON(http.StatusOK, string(eadBytes))
}

func doSm4Decryption(ctx *gin.Context) {
	ead := new(EncryptionAndDecryption)
	if err := ctx.BindJSON(&ead); err != nil {
		ctx.JSON(http.StatusBadRequest, fmt.Sprintf("could not find matching EncryptionAndDecryption form: %v", *ead))
		return
	}

	cipherBytes, err := hex.DecodeString(ead.CipherText)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, fmt.Sprintf("DecodeString error: %v", err))
		return
	}
	plainBytes, err := sm4.Sm4Ecb([]byte(ead.Key), cipherBytes, false) //sm4Ecb模式pksc7填充解密
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, fmt.Sprintf("Sm4Ecb error: %v", err))
		return
	}

	ead.Text = string(plainBytes)
	eadBytes, err := json.Marshal(*ead)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, fmt.Sprintf("ead marshal error: %v", err))
		return
	}

	ctx.JSON(http.StatusOK, string(eadBytes))
}

func doSm3Sum(ctx *gin.Context) {
	data := ctx.PostForm("data")
	if data == "" {
		ctx.JSON(http.StatusBadRequest, fmt.Sprintf("could not find matching data: %v", data))
		return
	}
	h := sm3.New()
	h.Write([]byte(data))
	sum := h.Sum(nil)

	ctx.JSON(http.StatusOK, sum)
}

func doSm2Encryption(ctx *gin.Context) {
	ead := new(EncryptionAndDecryption)
	if err := ctx.BindJSON(&ead); err != nil {
		ctx.JSON(http.StatusBadRequest, fmt.Sprintf("could not find matching EncryptionAndDecryption form: %v", *ead))
		return
	}
	r := strings.NewReader(ead.Key)
	priv, err := sm2.GenerateKey(r)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, fmt.Sprintf("generate key error: %v", err))
		return
	}

	cipherBytes, err := priv.PublicKey.EncryptAsn1([]byte(ead.Text), rand.Reader)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, fmt.Sprintf("EncryptAsn1 error: %v", err))
		return
	}

	ead.CipherText = string(hex.EncodeToString(cipherBytes))
	eadBytes, err := json.Marshal(*ead)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, fmt.Sprintf("ead marshal error: %v", err))
		return
	}

	ctx.JSON(http.StatusOK, string(eadBytes))
}

func doSm2Decryption(ctx *gin.Context) {
	ead := new(EncryptionAndDecryption)
	if err := ctx.BindJSON(&ead); err != nil {
		ctx.JSON(http.StatusBadRequest, fmt.Sprintf("could not find matching EncryptionAndDecryption form: %v", *ead))
		return
	}
	r := strings.NewReader(ead.Key)
	priv, err := sm2.GenerateKey(r)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, fmt.Sprintf("generate key error: %v", err))
		return
	}

	cipherTextBytes, err := hex.DecodeString(ead.CipherText)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, fmt.Sprintf("DecodeString error: %v", err))
		return
	}
	plainBytes, err := priv.DecryptAsn1(cipherTextBytes) //sm2解密
	if err != nil {
		ctx.JSON(http.StatusBadRequest, fmt.Sprintf("DecryptAsn1 error: %v", *ead))
		return
	}

	ead.Text = string(plainBytes)
	eadBytes, err := json.Marshal(*ead)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, fmt.Sprintf("ead marshal error: %v", err))
		return
	}

	ctx.JSON(http.StatusOK, string(eadBytes))
}
